#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
API客户端模块
负责与外部API接口的通信和数据获取
"""

import logging
import requests
import time
import yaml
from typing import Dict, Any, Optional, List
from datetime import datetime, timedelta
import json

class APIClient:
    """
    API客户端
    负责与外部API接口通信
    """
    
    def __init__(self, config_path: str = 'config.yaml'):
        """
        初始化API客户端
        
        Args:
            config_path: 配置文件路径
        """
        self.logger = logging.getLogger(__name__)
        self.config = self._load_config(config_path)
        self.api_config = self.config['api']
        self.session = self._create_session()
    
    def _load_config(self, config_path: str) -> Dict[str, Any]:
        """
        加载配置文件
        
        Args:
            config_path: 配置文件路径
            
        Returns:
            配置字典
        """
        try:
            with open(config_path, 'r', encoding='utf-8') as f:
                config = yaml.safe_load(f)
            return config
        except Exception as e:
            self.logger.error(f"加载配置文件失败: {e}")
            raise
    
    def _create_session(self) -> requests.Session:
        """
        创建HTTP会话
        
        Returns:
            requests.Session对象
        """
        session = requests.Session()
        
        # 设置默认headers
        session.headers.update(self.api_config['headers'])
        
        # 设置超时
        session.timeout = self.api_config.get('timeout', 30)
        
        return session
    
    def _make_request_with_retry(self, method: str, url: str, **kwargs) -> requests.Response:
        """
        带重试机制的HTTP请求
        
        Args:
            method: HTTP方法
            url: 请求URL
            **kwargs: 其他请求参数
            
        Returns:
            响应对象
        """
        retry_times = self.api_config.get('retry_times', 3)
        retry_delay = self.api_config.get('retry_delay', 5)
        
        last_exception = None
        
        for attempt in range(retry_times + 1):
            try:
                self.logger.debug(f"发起HTTP请求，尝试次数: {attempt + 1}/{retry_times + 1}")
                
                response = self.session.request(method, url, **kwargs)
                
                # 检查HTTP状态码
                if response.status_code == 200:
                    return response
                else:
                    self.logger.warning(f"HTTP请求返回非200状态码: {response.status_code}")
                    response.raise_for_status()
                    
            except requests.exceptions.RequestException as e:
                last_exception = e
                self.logger.warning(f"HTTP请求失败，尝试次数: {attempt + 1}, 错误: {e}")
                
                if attempt < retry_times:
                    self.logger.info(f"等待 {retry_delay} 秒后重试...")
                    time.sleep(retry_delay)
                else:
                    self.logger.error(f"HTTP请求重试次数已用完，最终失败")
                    break
        
        # 如果所有重试都失败，抛出最后一个异常
        if last_exception:
            raise last_exception
        else:
            raise requests.exceptions.RequestException("HTTP请求失败，未知错误")
    
    def get_project_data(self, update_time: Optional[str] = None, 
                        is_full: int = 0, page: int = 1, 
                        limit: int = 999999999, project_code: Optional[List[str]] = None) -> Dict[str, Any]:
        """
        获取项目数据
        
        Args:
            update_time: 更新时间，格式: YYYY-MM-DD
            is_full: 是否全量同步，0为增量，1为全量
            page: 页码
            limit: 每页数量
            
        Returns:
            API响应数据
        """
        try:
            # 如果没有指定更新时间，使用配置中的默认时间
            if not update_time:
                update_time = self.config['sync'].get('default_update_time', 
                                                    datetime.now().strftime('%Y-%m-%d'))
            
            # 构建请求参数
            request_data = {
                'page': page,
                'limit': limit,
                'update_time': update_time,
                'is_full': is_full
            }
            
            # 如果指定了项目代码，添加到请求参数中
            if project_code:
                request_data['projectCode'] = project_code
            
            
            
            self.logger.info(f"开始获取项目数据，参数: {request_data}")
            
            # 发起POST请求
            response = self._make_request_with_retry(
                'POST',
                self.api_config['base_url'],
                json=request_data,
                timeout=self.api_config.get('timeout', 30)
            )
            
            # 解析响应
            response_data = response.json()
            
            # 检查API响应状态
            if response_data.get('code') != 0:
                error_msg = f"API返回错误，code: {response_data.get('code')}, msg: {response_data.get('msg')}"
                self.logger.error(error_msg)
                raise APIException(error_msg)
            
            # 获取数据
            data = response_data.get('data', {})
            total = data.get('total', 0)
            data_list = data.get('list', [])
            
            self.logger.info(f"成功获取项目数据，总数: {total}, 当前批次: {len(data_list)}")
            
            return {
                'total': total,
                'list': data_list,
                'page': page,
                'limit': limit
            }
            
        except requests.exceptions.RequestException as e:
            self.logger.error(f"HTTP请求失败: {e}")
            raise APIException(f"HTTP请求失败: {e}")
        except json.JSONDecodeError as e:
            self.logger.error(f"JSON解析失败: {e}")
            raise APIException(f"JSON解析失败: {e}")
        except Exception as e:
            self.logger.error(f"获取项目数据失败: {e}")
            raise APIException(f"获取项目数据失败: {e}")
    
    def get_incremental_data(self, last_sync_time: Optional[str] = None) -> Dict[str, Any]:
        """
        获取增量数据
        
        Args:
            last_sync_time: 上次同步时间
            
        Returns:
            增量数据
        """
        # 如果没有上次同步时间，使用昨天的日期
        if not last_sync_time:
            yesterday = datetime.now() - timedelta(days=1)
            last_sync_time = yesterday.strftime('%Y-%m-%d')
        else:
            # 如果传入的是datetime格式，转换为日期格式
            if ' ' in last_sync_time:
                last_sync_time = last_sync_time.split(' ')[0]
        
        self.logger.info(f"获取增量数据，基准时间: {last_sync_time}")
        
        return self.get_project_data(
            update_time=last_sync_time,
            is_full=0  # 增量同步
        )
    
    def get_full_data(self) -> Dict[str, Any]:
        """
        获取全量数据
        
        Returns:
            全量数据
        """
        self.logger.info("获取全量数据")
        
        # 全量同步时也需要提供update_time参数，使用当前日期
        current_date = datetime.now().strftime('%Y-%m-%d')
        
        return self.get_project_data(
            update_time=current_date,
            is_full=1  # 全量同步
        )
    
    def test_connection(self) -> bool:
        """
        测试API连接
        
        Returns:
            连接是否成功
        """
        try:
            self.logger.info("测试API连接...")
            
            # 使用最小参数测试连接
            test_data = {
                'page': 1,
                'limit': 1,
                'update_time': datetime.now().strftime('%Y-%m-%d'),
                'is_full': 0
            }
            
            response = self._make_request_with_retry(
                'POST',
                self.api_config['base_url'],
                json=test_data,
                timeout=10
            )
            
            response_data = response.json()
            
            if response_data.get('code') == 0:
                self.logger.info("API连接测试成功")
                return True
            else:
                self.logger.error(f"API连接测试失败，返回码: {response_data.get('code')}")
                return False
                
        except Exception as e:
            self.logger.error(f"API连接测试失败: {e}")
            return False
    
    def close(self):
        """
        关闭会话
        """
        if self.session:
            self.session.close()
            self.logger.info("API客户端会话已关闭")


class APIException(Exception):
    """
    API异常类
    """
    pass


class ProjectDataFetcher:
    """
    项目数据获取器
    专门用于获取和预处理项目数据
    """
    
    def __init__(self, api_client: APIClient):
        self.api_client = api_client
        self.logger = logging.getLogger(__name__)
    
    def fetch_by_biz_sys(self, biz_sys: str, last_sync_time: Optional[str] = None) -> List[Dict[str, Any]]:
        """
        根据业务系统获取数据
        
        Args:
            biz_sys: 业务系统名称
            last_sync_time: 上次同步时间
            
        Returns:
            过滤后的数据列表
        """
        try:
            # 获取增量数据
            response_data = self.api_client.get_incremental_data(last_sync_time)
            all_data = response_data.get('list', [])
            
            # 根据bizSys过滤数据
            filtered_data = []
            for item in all_data:
                if item.get('bizSys') == biz_sys:
                    filtered_data.append(item)
            
            self.logger.info(f"业务系统 '{biz_sys}' 过滤后数据量: {len(filtered_data)}")
            return filtered_data
            
        except Exception as e:
            self.logger.error(f"根据业务系统获取数据失败: {e}")
            raise
    
    def fetch_bjhl_info_disclosure_data(self, last_sync_time: Optional[str] = None) -> List[Dict[str, Any]]:
        """
        获取互联信息披露数据
        
        Args:
            last_sync_time: 上次同步时间
            
        Returns:
            互联信息披露数据列表
        """
        return self.fetch_by_biz_sys('互联信息披露', last_sync_time)


class OperationalAPIClient:
    """
    房源运营信息API客户端
    负责与房源运营信息接口通信
    """
    
    def __init__(self, config_path: str = 'config.yaml'):
        """
        初始化房源运营信息API客户端
        
        Args:
            config_path: 配置文件路径
        """
        self.logger = logging.getLogger(__name__)
        self.config = self._load_config(config_path)
        self.api_config = self.config['operational_api']
        self.session = self._create_session()
    
    def _load_config(self, config_path: str) -> Dict[str, Any]:
        """
        加载配置文件
        
        Args:
            config_path: 配置文件路径
            
        Returns:
            配置字典
        """
        try:
            with open(config_path, 'r', encoding='utf-8') as f:
                config = yaml.safe_load(f)
            return config
        except Exception as e:
            self.logger.error(f"加载配置文件失败: {e}")
            raise
    
    def _create_session(self) -> requests.Session:
        """
        创建HTTP会话
        
        Returns:
            requests.Session对象
        """
        session = requests.Session()
        
        # 设置默认headers
        session.headers.update(self.api_config['headers'])
        
        # 设置超时
        session.timeout = self.api_config.get('timeout', 30)
        
        return session
    
    def _make_request_with_retry(self, method: str, url: str, **kwargs) -> requests.Response:
        """
        带重试机制的HTTP请求
        
        Args:
            method: HTTP方法
            url: 请求URL
            **kwargs: 其他请求参数
            
        Returns:
            响应对象
        """
        retry_times = self.api_config.get('retry_times', 3)
        retry_delay = self.api_config.get('retry_delay', 5)
        
        last_exception = None
        
        for attempt in range(retry_times + 1):
            try:
                self.logger.debug(f"发起HTTP请求，尝试次数: {attempt + 1}/{retry_times + 1}")
                
                response = self.session.request(method, url, **kwargs)
                
                # 检查HTTP状态码
                if response.status_code == 200:
                    return response
                else:
                    self.logger.warning(f"HTTP请求返回非200状态码: {response.status_code}")
                    response.raise_for_status()
                    
            except requests.exceptions.RequestException as e:
                last_exception = e
                self.logger.warning(f"HTTP请求失败，尝试次数: {attempt + 1}, 错误: {e}")
                
                if attempt < retry_times:
                    self.logger.info(f"等待 {retry_delay} 秒后重试...")
                    time.sleep(retry_delay)
                else:
                    self.logger.error(f"HTTP请求重试次数已用完，最终失败")
                    break
        
        # 如果所有重试都失败，抛出最后一个异常
        if last_exception:
            raise last_exception
        else:
            raise requests.exceptions.RequestException("HTTP请求失败，未知错误")
    
    def get_operational_data(self, project_id_list: List[int], 
                           channel: Optional[str] = None) -> Dict[str, Any]:
        """
        获取房源运营信息数据
        
        Args:
            project_id_list: 项目ID列表
            channel: 渠道，默认使用配置中的channel
            
        Returns:
            API响应数据
        """
        try:
            if not project_id_list:
                self.logger.warning("项目ID列表为空")
                return {'code': 200, 'data': [], 'message': '项目ID列表为空'}
            
            # 使用配置中的channel或传入的channel
            if channel is None:
                channel = self.api_config.get('channel', 'sszt')
            
            # 构建请求参数
            request_data = {
                'projectIdList': project_id_list,
                'channel': channel
            }
            
            self.logger.info(f"开始获取房源运营信息，项目数量: {len(project_id_list)}, 渠道: {channel}")
            
            # 发起POST请求
            response = self._make_request_with_retry(
                'POST',
                self.api_config['base_url'],
                json=request_data,
                timeout=self.api_config.get('timeout', 30)
            )
            
            # 解析响应
            response_data = response.json()
            
            # 检查API响应状态
            if response_data.get('code') != 200:
                error_msg = f"API返回错误，code: {response_data.get('code')}, message: {response_data.get('message')}"
                self.logger.error(error_msg)
                raise APIException(error_msg)
            
            # 获取数据
            data = response_data.get('data', [])
            
            self.logger.info(f"成功获取房源运营信息，返回项目数: {len(data)}")
            
            return response_data
            
        except requests.exceptions.RequestException as e:
            self.logger.error(f"HTTP请求失败: {e}")
            raise APIException(f"HTTP请求失败: {e}")
        except json.JSONDecodeError as e:
            self.logger.error(f"JSON解析失败: {e}")
            raise APIException(f"JSON解析失败: {e}")
        except Exception as e:
            self.logger.error(f"获取房源运营信息失败: {e}")
            raise APIException(f"获取房源运营信息失败: {e}")
    
    def test_connection(self) -> bool:
        """
        测试API连接
        
        Returns:
            连接是否成功
        """
        try:
            self.logger.info("测试房源运营信息API连接...")
            
            # 使用最小参数测试连接
            test_data = {
                'projectIdList': [194046],  # 使用文档中的测试ID
                'channel': 'sszt'
            }
            
            response = self._make_request_with_retry(
                'POST',
                self.api_config['base_url'],
                json=test_data,
                timeout=10
            )
            
            response_data = response.json()
            
            if response_data.get('code') == 200:
                self.logger.info("房源运营信息API连接测试成功")
                return True
            else:
                self.logger.error(f"房源运营信息API连接测试失败，返回码: {response_data.get('code')}")
                return False
                
        except Exception as e:
            self.logger.error(f"房源运营信息API连接测试失败: {e}")
            return False
    
    def close(self):
        """
        关闭会话
        """
        if self.session:
            self.session.close()
            self.logger.info("房源运营信息API客户端会话已关闭")


class ProjectIdFetcher:
    """
    项目ID获取器
    从指定表中获取projectId
    """
    
    def __init__(self, db_manager):
        self.db_manager = db_manager
        self.logger = logging.getLogger(__name__)
    
    def get_project_ids_from_tables(self, table_names: List[str]) -> List[int]:
        """
        从指定表中获取所有projectId并去重
        
        Args:
            table_names: 表名列表
            
        Returns:
            项目ID列表
        """
        project_ids = set()
        
        for table_name in table_names:
            try:
                # 检查表是否存在projectId字段
                sql = f"""
                    SELECT COLUMN_NAME 
                    FROM INFORMATION_SCHEMA.COLUMNS 
                    WHERE TABLE_SCHEMA = DATABASE() 
                    AND TABLE_NAME = '{table_name}' 
                    AND COLUMN_NAME = 'projectId'
                """
                result = self.db_manager.execute_query(sql)
                
                if not result:
                    self.logger.warning(f"表 {table_name} 不存在projectId字段，跳过")
                    continue
                
                # 获取projectId
                sql = f"SELECT DISTINCT projectId FROM `{table_name}` WHERE projectId IS NOT NULL AND projectId != ''"
                results = self.db_manager.execute_query(sql)
                
                for row in results:
                    project_id = row['projectId']
                    # 尝试转换为整数
                    try:
                        if isinstance(project_id, str):
                            project_id = int(project_id)
                        elif isinstance(project_id, (int, float)):
                            project_id = int(project_id)
                        else:
                            continue
                        
                        if project_id > 0:
                            project_ids.add(project_id)
                    except (ValueError, TypeError):
                        self.logger.warning(f"无法转换projectId为整数: {project_id}")
                        continue
                
                self.logger.info(f"从表 {table_name} 获取到 {len([r for r in results if r['projectId']])} 个projectId")
                
            except Exception as e:
                self.logger.error(f"从表 {table_name} 获取projectId失败: {e}")
                continue
        
        project_id_list = list(project_ids)
        self.logger.info(f"共获取到 {len(project_id_list)} 个唯一的projectId")
        
        return project_id_list